/**
 * Anderground project - Subway maps processor application.
 * Copyright (C) 2008-2009 Eldar Abusalimov, Ilya Shurigyn
 * 
 * This file is part of Anderground.
 * 
 * Anderground is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * Anderground is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Anderground.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.anderground.modules.storage.xml;

import java.util.ArrayList;
import java.util.List;

import org.anderground.core.model.City;
import org.anderground.core.model.Description;
import org.anderground.core.model.IdBindingModelBuilder;
import org.anderground.core.model.Line;
import org.anderground.core.model.ModelData;
import org.anderground.core.model.ModelException;
import org.anderground.core.model.Station;
import org.anderground.core.model.Transfer;
import org.anderground.core.model.Tunnel;
import org.anderground.core.util.Coord;
import org.anderground.core.util.Time;

/**
 * The {@link IdBindingModelBuilder} subclass suitable for using with
 * {@link SaxModelReader}.
 * 
 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
 */
public class SaxModelBuilder extends IdBindingModelBuilder {

	/**
	 * Temporary structure to store raw data about {@link Station} element.
	 * 
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	private class StationStub {
		int id = 0;
		Time open, close;
		String name;
		int labelX, labelY;
		int labelAngle;
		boolean labelVisible;
		Coord position;
		Coord location;
	}

	/**
	 * Temporary structure to store raw data about {@link Tunnel} element.
	 * 
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	private class TunnelStub {
		int from, to, line;
		int time;
		boolean smooth;
	}

	/**
	 * Temporary structure to store raw data about {@link Transfer} element
	 * read.
	 * 
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	private class TransferStub {
		int from, to;
		int time;
	}

	/**
	 * Temporary structure to store raw data about {@link Line} element.
	 * 
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	private class LineStub {
		int id = 0, color = 0xff777777;
		String name;
		int trainDelay = DEFAULT_TRAIN_DELAY;
	}

	/**
	 * Temporary structure to store raw data about {@link Description} element.
	 * 
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	private class DescriptionStub {
		String comment;
		List<String> authors = new ArrayList<String>();
	}

	/**
	 * Temporary structure to store raw data about {@link City} element.
	 * 
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	private class CityStub {
		String name, countryName, countryCode;
		List<String> aliases = new ArrayList<String>();
		public Coord location;
	}

	// TODO this should be specified in xml for each line element
	private final static int DEFAULT_TRAIN_DELAY = 100;

	/** temporary structure to write station data in */
	private StationStub stationStub = new StationStub();

	/** temporary structure to write line data in */
	private LineStub lineStub = new LineStub();

	private DescriptionStub descriptionStub = new DescriptionStub();

	private CityStub cityStub = new CityStub();

	/** List of Tunnel temporary structures */
	private List<TunnelStub> tunnelStubs = new ArrayList<TunnelStub>();

	/** List of Transfer temporary structures */
	private List<TransferStub> transferStubs = new ArrayList<TransferStub>();

	/**
	 * default constructor
	 */
	public SaxModelBuilder() {
		super();
	}

	@Override
	public ModelData build() throws ModelException {
		for (TunnelStub tunnelStub : tunnelStubs) {
			super.addTunnel(tunnelStub.from, tunnelStub.to, tunnelStub.time,
					tunnelStub.line, tunnelStub.smooth);
		}

		for (TransferStub transferStub : transferStubs) {
			super.addTransfer(transferStub.to, transferStub.from,
					transferStub.time);
		}

		setDescription(descriptionStub.authors, descriptionStub.comment);
		setCity(cityStub.name, cityStub.aliases, cityStub.countryCode,
				cityStub.countryName, cityStub.location);

		return super.build();
	}

	/**
	 * called when a new station element has occurred and it's id's retrieved
	 * 
	 * @param id
	 */
	public void startStation(int id) {
		this.stationStub.id = id;
		this.stationStub.position = null;
		this.stationStub.location = null;
		this.stationStub.open = null;
		this.stationStub.close = null;
		this.stationStub.name = null;
	}

	/**
	 * sets the new station's name
	 * 
	 * @param name
	 */
	public void setStationName(String name) {
		this.stationStub.name = name;
	}

	/**
	 * sets the new station's label properties
	 * 
	 * @param dx
	 * @param dy
	 * @param angle
	 * @param visible
	 */
	public void setStationNameAttrs(int dx, int dy, int angle, boolean visible) {
		this.stationStub.labelX = dx;
		this.stationStub.labelY = dy;
		this.stationStub.labelAngle = angle;
		this.stationStub.labelVisible = visible;
	}

	/**
	 * sets the new station's position
	 * 
	 * @param x
	 * @param y
	 */
	public void setStationPosition(int x, int y) {
		this.stationStub.position = new Coord(x, y);
	}

	/**
	 * sets the new station's geo location
	 * 
	 * @param latitude
	 * @param longitude
	 */
	public void setStationLocation(int latitude, int longitude) {
		this.stationStub.location = new Coord(latitude, longitude);
	}

	/**
	 * sets the new station's open and close schedule
	 * 
	 * @param open
	 * @param close
	 */
	public void setStationSchedule(Time open, Time close) {
		this.stationStub.open = open;
		this.stationStub.close = close;
	}

	/**
	 * instantiates Station with retrieved parameters
	 * 
	 * @throws ModelException
	 */
	public void endStation() throws ModelException {
		super.addStation(stationStub.id, stationStub.name,
				stationStub.position, stationStub.location, stationStub.open,
				stationStub.close, stationStub.labelX, stationStub.labelY,
				stationStub.labelAngle, stationStub.labelVisible);
	}

	/**
	 * adds a new Tunnel to future model
	 * 
	 * @param to
	 * @param from
	 * @param time
	 * @param line
	 * @param smooth
	 */
	@Override
	public void addTunnel(int from, int to, int time, int line, boolean smooth) {
		TunnelStub tunnelStub = new TunnelStub();
		tunnelStubs.add(tunnelStub);
		tunnelStub.to = to;
		tunnelStub.from = from;
		tunnelStub.line = line;
		tunnelStub.time = time;
		tunnelStub.smooth = smooth;
	}

	/**
	 * adds a new Transfer to future model
	 * 
	 * @param to
	 * @param from
	 * @param time
	 */
	@Override
	public void addTransfer(int from, int to, int time) {
		TransferStub transferStub = new TransferStub();
		transferStubs.add(transferStub);
		transferStub.to = to;
		transferStub.from = from;
		transferStub.time = time;
	}

	/**
	 * called when a new line element has occurred and it's id's retrieved
	 * 
	 * @param id
	 */
	public void startLine(int id) {
		this.lineStub.id = id;
	}

	/**
	 * sets the new line's name
	 * 
	 * @param name
	 */
	public void setLineName(String name) {
		this.lineStub.name = name;
	}

	/**
	 * sets the new line's color
	 * 
	 * @param color
	 */
	public void setLineColor(int color) {
		this.lineStub.color = color;
	}

	/**
	 * sets the trains delay for the new line
	 * 
	 * @param delay
	 *            in seconds
	 */
	public void setLineDelay(int delay) {
		this.lineStub.trainDelay = delay;
	}

	/**
	 * instantiates Line with retrieved parameters
	 */
	public void endLine() {
		super.addLine(lineStub.id, lineStub.name, lineStub.color,
				lineStub.trainDelay);
	}

	/**
	 * called when the city's name retrieved
	 * 
	 * @param cityName
	 */
	public void setCityName(String cityName) {
		cityStub.name = cityName;
	}

	/**
	 * adds a new city name alias
	 * 
	 * @param alias
	 */
	public void addCityAlias(String alias) {
		cityStub.aliases.add(alias);
	}

	/**
	 * @param latitude
	 * @param longitude
	 */
	public void setCityLocation(int latitude, int longitude) {
		cityStub.location = new Coord(latitude, longitude);
	}

	/**
	 * called when the country's name retrieved
	 * 
	 * @param countryName
	 */
	public void setCountryName(String countryName) {
		cityStub.countryName = countryName;
	}

	/**
	 * sets the country code
	 * 
	 * @param code
	 */
	public void setCountryCode(String code) {
		cityStub.countryCode = code;
	}

	/**
	 * called when the author name retrieved
	 * 
	 * @param authorName
	 */
	public void addAuthorName(String authorName) {
		descriptionStub.authors.add(authorName);
	}

	/**
	 * 
	 * @param note
	 */
	public void setNote(String note) {
		descriptionStub.comment = note;
	}

}
